package be.kuleuven.cs.mop.app;


import be.kuleuven.cs.mop.domain.exceptions.InvalidCurrentUserException;
import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.impl.AdminUserTypeImpl;
import be.kuleuven.cs.mop.domain.model.impl.UserImpl;
import be.kuleuven.cs.mop.domain.model.impl.World;



/**
 * The system class
 * Used to bundle important information at controllers-level
 */
public class System {
	
	/**
	 * The world object that is used by multiple controllers 
	 */
	private final World	world;
	
	/**
	 * Current user instantiated as a null object, to represent that there is no current user
	 */
	private UserImpl currentUser = null;
	
	/**
	 * Creates a new system and a new world
	 */
	public System() {
		this(new World());
	}
	
	/**
	 * Creates a new system
	 * @param world - The given World
	 */
	public System(final World world) {
		if (world == null)
			throw new NullPointerException("World == NULL");
		this.world = world;
		
		//	Create temporary admin user
		//	mainly for parsing purposes
		try {
			setUser(new UserImpl(getWorld(), "SUPERADMIN",
					new AdminUserTypeImpl(getWorld())));
		} catch (final TaskManagerException e) {
			//	should never be reached
			e.printStackTrace();
			assert false;
			throw new InternalError();
		}
	}
	
	/**
	 * Returns the <code>World</code>
	 */
	public World getWorld() { return world; }
	
	/**
	 * Returns the current <code>User</code> of the system
	 * @throws InvalidCurrentUserException 
	 */
	public UserImpl getCurrentUser() throws InvalidCurrentUserException { 
		
		if(!isValidUser(currentUser))
			throw new InvalidCurrentUserException(InvalidCurrentUserException.NO_USER_LOGGED_IN);
		
			return currentUser; 
	}
	
	/**
	 * Checks whether there is a current user
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - !{@link #isValidUser(UserImpl)}
	 */
	public void checkIfUserIsLoggedIn() throws InvalidCurrentUserException{
		if(!isValidUser(currentUser))
			throw new InvalidCurrentUserException(InvalidCurrentUserException.NO_USER_LOGGED_IN);
	}
	
	/**
	 * Checks whether there is a current user, and that it's an admin user
	 * 
	 * @throws InvalidCurrentUserException NO_USER_LOGGED_IN - !{@link #isValidUser(UserImpl)}
	 * @throws InvalidCurrentUserException USER_NOT_QUALIFIED_TO_EXECUTE - The current user is not an admin 
	 */
	public void checkIfCurrentUserIsAdmin() throws InvalidCurrentUserException{
		if(!getCurrentUser().isAdmin())
			throw new InvalidCurrentUserException(InvalidCurrentUserException.USER_NOT_QUALIFIED_TO_EXECUTE);
	}
	
	
	/**
	 * Sets the current <code>User</code> of the system
	 * 
	 * @throws TaskManagerException INVALID_USER - !{@link #isValidUser(UserImpl)}
	 */
	public void setUser(final UserImpl user) throws TaskManagerException {
		if (!isValidUser(user))
			throw new TaskManagerException(TaskManagerException.INVALID_USER);
		this.currentUser = user;
	}
	
	/**
	 * Validates the given User
	 * @param user - The given User
	 * 
	 * @return false if user is null object or does not exist in the system
	 */
	private boolean isValidUser(UserImpl user) {
		return (user != null && getWorld().isPresent(user));
	}
	
	/**
	 * Logs out the currently logged in user, if any
	 */
	public void logout() {
		this.currentUser = null;
	}
	
}